package com.ima.common.repository.support;

import org.hibernate.Session;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class GenericRepository<E, PK extends Serializable> extends SimpleJpaRepository<E, PK> implements IGenericRepository<E, PK> {

    protected EntityManager em;

    public GenericRepository(JpaEntityInformation<E, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.em = entityManager;
    }

    public GenericRepository(Class<E> domainClass, EntityManager em) {
        this(JpaEntityInformationSupport.getMetadata(domainClass, em), em);
    }

    @Override
    public EntityManager getEntityManager() {
        return em;
    }

    @Override
    public E findOne(PK id, LockModeType lockMode) {
        return em.find(getDomainClass(), id, lockMode);
    }

    @Override
    public E findOne(Specification<E> spec, LockModeType lockMode) {
        try {
            return getQuery(spec, (Sort) null).setLockMode(lockMode).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    @Override
    public List<E> findTop(int top, Specification<E> spec, Sort sort) {
        return findTop(top, spec, sort, LockModeType.NONE);
    }

    @Override
    public List<E> findTop(int top, Specification<E> spec, Sort sort, LockModeType lockMode) {
        return getQuery(spec, sort).setLockMode(lockMode).setMaxResults(top).getResultList();
    }

    @Override
    public <S extends E> S update(S entity) {
        Session session = em.unwrap(Session.class);
        session.update(entity); //使用update方法提高效率
        return entity;
    }

    @Override
    public int updateByHQL(String hql, Map<String, Object> parameters) {
        Query query = em.createQuery(hql);
        if (parameters != null) {
            Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Object> entry = it.next();
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.executeUpdate();
    }

	/*@SuppressWarnings("unchecked")
    @Override
	public <T> List<T> nativeSqlResultDTO(Class<T> dtoClass, String sql, final Map<String, Object> params) {
		return nativeSqlResultDTO(dtoClass, sql, params, null);
	}*/

    @SuppressWarnings("unchecked")
    public <T> List nativeSqlResult(String sql, final Map<String, Object> params) {
        Query query = em.createNativeQuery(sql);
        if (null != params && params.size() > 0) {
            for (String key : params.keySet()) {
                query.setParameter(key, params.get(key));
            }
        }
        return query.getResultList();
    }

    @Override
    public int updateNativeSql(String sql, Map<String, Object> parameters) {
        Query query = em.createNativeQuery(sql);
        if (parameters != null) {
            Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Object> entry = it.next();
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.executeUpdate();
    }

    /**
     * 更新对象， 只更新指定的属性。根据id主键更新
     * columns: 你要更新的属性, 名称要和entity一致。
     */
    /*@Override
    public boolean updateColumn(E entity, String... columns) throws Exception {
        Class<?> clazz = entity.getClass();
        String tableName = clazz.getAnnotation(Table.class).name();
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        StringBuffer sql = new StringBuffer("update " + tableName + " set ");
        boolean isFirse = true;

        for (String column : columns) {
            Field field = clazz.getDeclaredField(column);
            String columnName = field.getAnnotation(Column.class).name();

            if (isFirse) {
                sql.append(columnName).append(" =:").append(column);
                isFirse = false;
            } else {
                sql.append(", ").append(columnName).append(" =:").append(column);
            }

            field.setAccessible(true);
            Object value = field.get(entity);

            if (value == null) {

            } else if (value instanceof PersistEnum) {
                value = ((PersistEnum<?>) value).getPersistedValue();
            } else if (value instanceof Date) {
                Date date = (Date) value;
                value = new java.sql.Timestamp(date.getTime());
            }

            paramsMap.put(column, value);
        }

        sql.append(" where id = ").append(entity.getId());

        int i = updateNativeSql(sql.toString(), paramsMap);

        return i > 0;
    }*/

}
